package sysService

import (
	"context"
	"encoding/json"
	"fmt"
	"gitee.com/jokces/kit/enum"
	"gitee.com/jokces/kit/errc"
	"gitee.com/jokces/kit/global"
	sysModel "gitee.com/jokces/kit/global/admin/model"
	"gitee.com/jokces/kit/global/admin/util"
	"gitee.com/jokces/kit/typ"
	"strings"
	"time"
)

// MenuTreeList 菜单
func (svc *Service) MenuTreeList(ctx context.Context, in *sysModel.MenusListSelectedReq) ([]*sysModel.SysMenu, error) {
	menus, err := svc.d.MenuList(ctx, &sysModel.SysMenu{
		Status:      in.Status,
		MenuTypeArr: in.GetMenuTypes(),
		MenuName:    in.MenuName,
		GroupId:     enum.DicEnum(in.GroupId),
	})
	if err != nil {
		return nil, errc.WithStack(err)
	}
	//处理菜单的排序状态
	recursive := GetTreeRecursive(menus, 0)
	return recursive, nil
}
func GetTreeRecursive(list []*sysModel.SysMenu, parentId int64) []*sysModel.SysMenu {
	res := make([]*sysModel.SysMenu, 0)
	for _, v := range list {
		if v.ParentId == parentId {
			v.Children = GetTreeRecursive(list, v.Id)
			res = append(res, v)
		}
	}
	return res
}
func (svc *Service) MenuAdd(ctx context.Context, req *sysModel.MenuAddReq) error {
	newMenu := sysModel.SysMenu{
		Status:     req.Status,
		MenuName:   req.MenuName,
		Sequence:   req.Sequence,
		Title:      req.Title,
		Icon:       req.Icon,
		MenuType:   req.MenuType,
		Show:       req.Show,
		Component:  req.Component,
		GroupId:    enum.DicEnum(req.GroupId),
		Permission: req.Permission,
	}
	if req.ParentId > 0 {
		if exist, m, err := svc.d.MenuGetOne(ctx, &sysModel.SysMenu{
			Id: req.ParentId,
		}); err != nil {
			return errc.WithStack(err)
		} else if !exist {
			return errc.ErrParamInvalid.MultiMsg("上级菜单不存在")
		} else if m.Status != enum.ABLE {
			return errc.ErrParamInvalid.MultiMsg("上级菜单已停用")
		} else {
			newMenu.ParentId = m.Id
		}
	}
	return global.GetDb().TxInsert(nil, newMenu)
}
func (svc *Service) MenuEdit(ctx context.Context, req *sysModel.MenuEditReq) error {
	newMenu := sysModel.SysMenu{
		Status:    req.Status,
		MenuName:  req.MenuName,
		Sequence:  req.Sequence,
		Title:     req.Title,
		Icon:      req.Icon,
		MenuType:  req.MenuType,
		Show:      req.Show,
		Component: req.Component,
	}
	if req.ParentId > 0 {
		if exist, m, err := svc.d.MenuGetOne(ctx, &sysModel.SysMenu{
			Id: req.ParentId,
		}); err != nil {
			return errc.WithStack(err)
		} else if !exist {
			return errc.ErrParamInvalid.MultiMsg("上级菜单不存在")
		} else if m.Status != enum.ABLE {
			return errc.ErrParamInvalid.MultiMsg("上级菜单已停用")
		} else {
			newMenu.ParentId = m.Id
		}
	}
	exist, _, err := svc.d.MenuGetOne(ctx, &sysModel.SysMenu{
		Id: req.Id,
	})
	if err != nil {
		return errc.WithStack(err)
	}
	if !exist {
		return errc.ErrParamInvalid.MultiMsg("菜单不存在")
	}
	if err := svc.d.GetDao().TxBeanUpdate(nil, req.Id, newMenu); err != nil {
		return errc.WithStack(err)
	}
	return nil
}
func (svc *Service) MenuDelete(ctx context.Context, id int64) error {
	exist, _, err := svc.d.MenuGetOne(ctx, &sysModel.SysMenu{
		Id: id,
	})
	if err != nil {
		return errc.WithStack(err)
	}
	if !exist {
		return errc.ErrParamInvalid.MultiMsg("菜单不存在")
	}

	list, err := svc.d.MenuList(ctx, &sysModel.SysMenu{
		ParentId: id,
	})
	if err != nil {
		return errc.WithStack(err)
	}
	if len(list) > 0 {
		return errc.ErrParamInvalid.MultiMsg("先删除子菜单")
	}
	ids := make([]int64, 0)
	for _, menu := range list {
		ids = append(ids, menu.Id)
	}
	ids = append(ids, id)
	return global.GetDb().TxBatchDelete(nil, ids, sysModel.SysMenu{})
}

// ActionPageList 资源功能
func (svc *Service) ActionPageList(ctx context.Context, in *sysModel.ActionListQueryReq) (typ.ListResp, error) {
	rs := typ.ListResp{}
	row, list, err := svc.d.ActionPageList(ctx, &sysModel.SysAction{
		Status:  in.Status,
		ApiName: in.ApiName,
		GroupId: enum.DicEnum(in.GroupId),
	}, in.GetPage())
	if err != nil {
		return rs, errc.WithStack(err)
	}
	rs.Count = row
	rs.List = list
	return rs, nil
}
func (svc *Service) ActionSaveOrUpdate(ctx context.Context, req *sysModel.ActionEditReq) error {
	if req.Status > 2 || req.Status < 1 {
		return errc.ErrParamInvalid.MultiMsg("状态不正确")
	}
	sa := sysModel.SysAction{
		Id:      req.Id,
		Api:     req.Api,
		ApiName: req.ApiName,
		Status:  req.Status,
		GroupId: enum.DicEnum(req.GroupId),
	}

	if req.Id > 0 {
		exist, api, err := svc.d.ActionGetOne(ctx, &sysModel.SysAction{
			Id: req.Id,
		})
		if err != nil {
			return err
		}
		if !exist {
			return errc.ErrParamInvalid.MultiMsg("资源不存在")
		}
		if err := global.GetDb().TxBeanUpdate(nil, api.Id, sa); err != nil {
			return err
		}
		return nil
	}

	if exist, _, err := svc.d.ActionGetOne(ctx, &sysModel.SysAction{
		Api: req.ApiName,
	}); err != nil {
		return err
	} else if exist {
		return errc.ErrParamInvalid.MultiMsg("该资源已存在")
	}
	sa.Code = util.Md516(sa.Api)
	if err := svc.d.GetDao().TxInsert(nil, sa); err != nil {
		return err
	}
	return nil
}
func (svc *Service) ActionDel(ctx context.Context, id int64) error {
	if exist, _, err := svc.d.ActionGetOne(ctx, &sysModel.SysAction{
		Id: id,
	}); err != nil {
		return err
	} else if exist {
		if err := global.GetDb().TxDelete(nil, id, sysModel.SysAction{}); err != nil {
			return errc.WithStack(err)
		}
	}
	return nil
}
func (svc *Service) ActionAddDoc(ctx context.Context, req *sysModel.ActionAddDocReq) error {
	s := &sysModel.SwaggerJSON{}
	if err := json.Unmarshal([]byte(req.Docs), s); err != nil {
		return errc.ErrParamInvalid.MultiMsg(fmt.Sprintf("swaggerTxt %v", err))
	}
	for p, methods := range s.Paths {
		if req.Prefix != "" && !strings.HasPrefix(p, req.Prefix) {
			continue
		}
		if req.Expel != "" && strings.Contains(p, req.Expel) {
			continue
		}
		for _, c := range methods {
			ac := &sysModel.SysAction{
				Api:     p,
				ApiName: c.Summary,
				Code:    util.Md516(p),
				Status:  enum.ABLE,
				GroupId: enum.DicEnum(req.GroupId),
			}
			if exist, requests, err := svc.d.ActionGetOne(ctx, &sysModel.SysAction{
				Api: p,
			}); err != nil {
				return errc.WithStack(err)
			} else if exist {
				//直接进行修改
				if err := global.GetDb().TxUpdate(nil, requests.Id, sysModel.SysAction{
					ApiName: ac.ApiName,
				}, []string{"api_name"}); err != nil {
					return errc.WithStack(err)
				}
			} else {
				if err := global.GetDb().TxInsert(nil, ac); err != nil {
					return errc.WithStack(err)
				}
			}
		}
	}
	return nil
}

// ActionWithTitle 根据API来获取对应的资源信息
func (svc *Service) ActionWithTitle(api string) (string, error) {
	rs := make([]string, 0)
	result, err := svc.r.KeyWithCache(10*time.Minute, rs)(context.Background(), sysModel.AdminActionAble, func() (interface{}, error) {
		_, actions, err := svc.d.ActionPageList(context.Background(), &sysModel.SysAction{Status: enum.ABLE}, nil)
		if err != nil {
			return nil, err
		}
		apiArr := make([]string, 0)
		for _, action := range actions {
			apiArr = append(apiArr, action.ApiName)
		}
		return apiArr, err
	})
	if err != nil {
		return "", err
	}

	if result != nil {
		rs = result.([]string)
		for _, action := range rs {
			if action == api {
				return action, nil
			}
		}
	}
	return "", nil
}

// MenuBindActionAll 绑定相关的接口
func (svc *Service) MenuBindActionAll(ctx context.Context, menuId int64) ([]*sysModel.SysAction, error) {
	list := make([]*sysModel.SysAction, 0)
	menus, err := svc.d.MenuBindActionAll(ctx, menuId)
	if err != nil {
		return list, err
	}
	return menus, nil
}
func (svc *Service) MenuBindAction(ctx context.Context, req *sysModel.BindMenuWithActionReq) error {
	exist, menu, err := svc.d.MenuGetOne(ctx, &sysModel.SysMenu{
		Id: req.MenuId,
	})
	if err != nil {
		return errc.WithStack(err)
	}
	if !exist {
		return errc.ErrParamInvalid.MultiMsg("菜单不存在")
	}

	ids, err := svc.d.ActionListByIds(ctx, req.Ids)
	if err != nil {
		return errc.WithStack(err)
	}
	if len(ids) != len(req.Ids) {
		return errc.ErrParamInvalid.MultiMsg("资源不匹配")
	}
	return svc.d.MenuBindActions(ctx, menu, ids)
}

// RolePageList 角色
func (svc *Service) RolePageList(ctx context.Context, req *sysModel.RoleListPageReq) (typ.ListResp, error) {
	rs := typ.ListResp{}
	query := &sysModel.SysRole{
		LikeRoleName: req.RoleName,
		Status:       req.Status,
	}
	count, rol, err := svc.d.RolePageList(ctx, query, req.GetPage())
	if err != nil {
		return rs, errc.WithStack(err)
	}
	rs.List = rol
	rs.Count = count
	return rs, nil
}
func (svc *Service) RoleAdd(ctx context.Context, req *sysModel.RoleAddReq) error {
	if req.Typ < sysModel.ALL || req.Typ > sysModel.SELF {
		return errc.ErrParamInvalid.MultiMsg("不正确的角色类型")
	}
	rl := sysModel.SysRole{
		Status:   req.Status,
		RoleName: req.RoleName,
		RoleCode: req.RoleCode,
		Typ:      req.Typ,
	}

	if exist, _, err := svc.d.RoleGetOne(ctx, &sysModel.SysRole{
		RoleName: req.RoleName,
	}); err != nil {
		return errc.WithStack(err)
	} else if exist {
		return errc.ErrParamInvalid.MultiMsg("角色名已存在")
	}

	err := svc.d.GetDao().TxInsert(nil, rl)
	return errc.WithStack(err)
}
func (svc *Service) RoleEdit(ctx context.Context, req *sysModel.RoleEditReq) error {
	if req.Typ < sysModel.CHILDREN || req.Typ > sysModel.SELF {
		return errc.ErrParamInvalid.MultiMsg("不正确的角色类型")
	}
	rl := sysModel.SysRole{
		Status:   req.Status,
		RoleName: req.RoleName,
		RoleCode: req.RoleCode,
		Typ:      req.Typ,
	}

	exist, _, err := svc.d.RoleGetOne(ctx, &sysModel.SysRole{
		Id: req.Id,
	})
	if err != nil {
		return err
	}
	if !exist {
		return errc.ErrParamInvalid.MultiMsg("未找到角色")
	}
	err = global.GetDb().TxBeanUpdate(nil, req.Id, rl)
	return errc.WithStack(err)
}
func (svc *Service) RoleDel(ctx context.Context, roleId int64) error {
	exist, role, err := svc.d.RoleGetOne(ctx, &sysModel.SysRole{
		Id: roleId,
	})
	if err != nil {
		return errc.WithStack(err)
	}
	if !exist {
		return errc.ErrParamInvalid.MultiMsg("角色不存在")
	}
	if role.IsRoot == 1 {
		return errc.ErrParamInvalid.MultiMsg("超级管理员不能删除")
	}

	if err := global.GetDb().TxBeanUpdate(nil, role.Id, &sysModel.SysRole{
		Status: enum.DISABLED,
		IsDel:  int32(enum.DEL),
	}); err != nil {
		return errc.WithStack(err)
	}
	return nil
}

// RolePermissionAll 所有的权限资源
func (svc *Service) RolePermissionAll(ctx context.Context, roleId int64) (*sysModel.RoleActionsResp, error) {
	rs := &sysModel.RoleActionsResp{}
	menus := make([]*sysModel.RoleActionResp, 0)
	chked := make([]int64, 0)
	list, err := svc.RolePermissionList(ctx, roleId)
	if err != nil {
		return rs, err
	}
	for _, group := range list {
		menus = append(menus, group.ActList...)
		chked = append(chked, group.Checked...)
	}
	rs.Checked = chked
	rs.ActList = menus
	return rs, nil
}

// RolePermissionList 用户的权限资源
func (svc *Service) RolePermissionList(ctx context.Context, roleId int64) ([]*sysModel.MenuGroup, error) {
	rs := make([]*sysModel.MenuGroup, 0)
	//所有的权限资源
	menus, err := svc.d.MenuList(ctx, &sysModel.SysMenu{
		Status: enum.ABLE,
	})
	if err != nil {
		return rs, err
	}

	roleMenus, err := svc.d.MenuListByRoleId(ctx, roleId)
	if err != nil {
		return rs, err
	}
	return svc.makeMenuGroup(menus, roleMenus), nil
}
func (svc *Service) RoleBindAction(ctx context.Context, req *sysModel.RoleBindActionReq) error {
	exist, role, err := svc.d.RoleGetOne(ctx, &sysModel.SysRole{
		Id: req.RoleId,
	})
	if err != nil {
		return err
	}
	if !exist {
		return errc.ErrParamInvalid.MultiMsg("未找到角色")
	}
	if role.Status != enum.ABLE {
		return errc.ErrParamInvalid.MultiMsg("角色已停用")
	}
	if role.IsRoot == 1 {
		return errc.ErrParamInvalid.MultiMsg("超级管理员不能修改")
	}

	ids, err := svc.d.MenuListByIds(ctx, req.Ids)
	if err != nil {
		return errc.WithStack(err)
	}
	if len(ids) != len(req.Ids) {
		return errc.ErrParamInvalid.MultiMsg("菜单不匹配")
	}

	if err := svc.d.RoleBindMenus(ctx, role, ids); err != nil {
		return err
	}
	//处理删除对应的缓存
	key := fmt.Sprintf("%v:%v", sysModel.AdminActions, req.RoleId)
	_ = global.GetRedis().Del(key)
	return nil
}
func (svc *Service) RoleDataList(sess sysModel.AdminUserToken) []*sysModel.RoleDat {
	list := make([]*sysModel.RoleDat, 0)
	if sess.All() {
		list = append(list,
			&sysModel.RoleDat{
				Typ: sysModel.ALL,
				Val: "所有权限",
			},
			&sysModel.RoleDat{
				Typ: sysModel.CHILDREN,
				Val: "下级权限",
			},
			&sysModel.RoleDat{
				Typ: sysModel.SELF,
				Val: "本部门权限",
			},
		)
		return list
	}

	if sess.Children() {
		list = append(list,
			&sysModel.RoleDat{
				Typ: sysModel.CHILDREN,
				Val: "下级权限",
			},
			&sysModel.RoleDat{
				Typ: sysModel.SELF,
				Val: "本部门权限",
			},
		)
		return list
	}

	if sess.Self() {
		list = append(list,
			&sysModel.RoleDat{
				Typ: sysModel.SELF,
				Val: "本部门权限",
			},
		)
		return list
	}
	return list
}

func (svc *Service) RoleMenuMake(all, role []*sysModel.SysMenu) []*sysModel.SysMenu {
	m := make(map[int64]*sysModel.SysMenu)
	for _, menu := range role {
		svc.queryById(all, menu.Id, m)
	}
	rs := make([]*sysModel.SysMenu, 0)
	for _, menu := range m {
		rs = append(rs, menu)
	}
	return rs
}
func (svc *Service) queryById(all []*sysModel.SysMenu, menuId int64, m map[int64]*sysModel.SysMenu) {
	for _, menu := range all {
		if menu.Id == menuId {
			m[menu.Id] = menu
			if menu.ParentId != 0 && m[menu.ParentId] == nil {
				svc.queryById(all, menu.ParentId, m)
			}
			return
		}
	}
}
func (svc *Service) makeMenuGroup(all, checked []*sysModel.SysMenu) []*sysModel.MenuGroup {
	_, dics, _ := svc.d.DicList(context.Background(), &sysModel.SysDic{Key: "MENU_RES_GROUP"}, nil)
	mp := make(map[string]*sysModel.RoleActionsResp)
	for _, menu := range all {
		groups := mp[string(menu.GroupId)]
		if groups == nil {
			groups = &sysModel.RoleActionsResp{}
			mp[string(menu.GroupId)] = groups

			groups.ActList = make([]*sysModel.RoleActionResp, 0)
			groups.Checked = make([]int64, 0)
		}
		groups.ActList = append(groups.ActList, &sysModel.RoleActionResp{
			Id:        menu.Id,
			MenuName:  menu.MenuName,
			GroupName: svc.findGroupName(string(menu.GroupId), dics),
			GroupId:   string(menu.GroupId),
			Pid:       menu.ParentId,
		})

		exist := svc.findByMenuId(checked, menu.Id)
		if exist {
			groups.Checked = append(groups.Checked, menu.Id)
		}
	}
	out := make([]*sysModel.MenuGroup, 0)
	for key, resp := range mp {
		val := &sysModel.MenuGroup{
			GroupName: svc.findGroupName(key, dics),
			GroupId:   key,
			ActList:   resp.ActList,
			Checked:   resp.Checked,
		}
		out = append(out, val)
	}
	return out
}
func (svc *Service) findByMenuId(checked []*sysModel.SysMenu, menuId int64) bool {
	for _, menu := range checked {
		if menu.Id == menuId {
			return true
		}
	}
	return false
}
func (svc *Service) findGroupName(uk string, list []*sysModel.SysDic) string {
	for _, dic := range list {
		if dic.UniqueKey == uk {
			return dic.Value
		}
	}
	return ""
}
